package org.hawk.gwt.ppc.reflect.emul.java.lang.reflect;

import org.hawk.gwt.ppc.reflect.lang.ClassDelegate;

public final class Method extends AccessibleObject {
	private Object accessor;
	private ClassDelegate<?> clazz;
	private String signature;

	public Method(Object accessor, ClassDelegate<?> clazz, String signature) {
		checkAccess(accessor);
		this.accessor = accessor;
		this.clazz = clazz;
		this.signature = signature;
	}

	@Override
	public int getModifiers() {
		return clazz.getModifiers(accessor, signature);
	}

	public ClassDelegate<?> getDeclaringClass() {
		return clazz;
	}

	public String getName() {
		int i = signature.indexOf('(');
		if (i < 0)
			return signature;
		return signature.substring(0, i);
	}

	public Class<?> getReturnType() {
		return clazz.getReturnType(accessor, signature);
	}

	public Class<?>[] getParameterTypes() {
		return ClassDelegate.unmarshalSignature(signature);
	}

	public ClassDelegate<?>[] getExceptionTypes() {
		throw new UnsupportedOperationException();
	}

	public boolean equals(Object obj) {
		if (obj == null)
			return false;
		if (obj == this)
			return true;
		if (obj instanceof Method) {
			Method m = (Method) obj;
			return m.signature.equals(signature);
		}
		return false;
	}

	public int hashCode() {
		return signature.hashCode();
	}

	public String toString() {
		return signature;
	}

	public Object invoke(Object obj, Object... parameters)
			throws IllegalAccessException,
			java.lang.reflect.InvocationTargetException {
		checkAccess();
		checkParameters(parameters, getParameterTypes());
		if (obj != null && !clazz.isInstance(obj)) {
			throw new IllegalArgumentException(obj + " is not instance of "
					+ clazz);
		}
		try {
			return clazz.invoke(accessor, signature, obj, parameters);
		} catch (Throwable ex) {
			throw new java.lang.reflect.InvocationTargetException(ex);
		}
	}

	public boolean isBridge() {
		return false;
	}

	public boolean isVarArgs() {
		throw new UnsupportedOperationException();
	}

	public boolean isSynthetic() {
		throw new UnsupportedOperationException();
	}

	public boolean getAnnotation(Class<?> cls) {
		throw new UnsupportedOperationException();
	}

	public boolean getDeclaredAnnotations() {
		throw new UnsupportedOperationException();
	}

	public boolean getDefaultValue() {
		throw new UnsupportedOperationException();
	}

	public boolean getParameterAnnotations() {
		throw new UnsupportedOperationException();
	}

	static void checkParameters(Object[] parameters,
			Class<?>[] parameterTypes) {
		if (parameters.length != parameterTypes.length) {
			throw new IllegalArgumentException(
					"Wrong number of parameters: expected "
							+ parameterTypes.length + " but was "
							+ parameters.length);
		}
		for (int i = 0; i < parameters.length; i++) {
			if (parameters[i] == null) {
				if (parameterTypes[i].isPrimitive()) {
					throw new IllegalArgumentException(
							"Unable to cast NULL to " + parameterTypes[i]);
				}
			} else if (!ClassDelegate.forClass(parameters[i].getClass())
					.isAssignableFrom(parameterTypes[i])) {
				throw new IllegalArgumentException("Unable to cast "
						+ parameters[i].getClass() + " to " + parameterTypes[i]);
			}

		}
	}
}
